using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using NopSolutions.NopCommerce.BusinessLogic.Audit;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.LuceneHelper.Queries;

namespace NopSolutions.NopCommerce.LuceneHelper.Tasks
{
    public class ProductUpdateTask : BaseTask<ProductUpdateTask>
    {
        #region Private

        private Product _product;

        #endregion

        #region Properties
        
        private int ProductId
        {
            get { return Id; }
        }

        public bool ForceUpdate { get; set; }
        
        public Product Product
        {
            get { return _product ?? (_product = ProductManager.GetProductById(ProductId)); }
            set
            {
                _product = value;
            }
        }

        #endregion

        #region Constructors

        public ProductUpdateTask(int productId, Product product, bool forceUpdate)
            : base(productId)
        {
            Product = product;
            ForceUpdate = forceUpdate;
        }

        public ProductUpdateTask(Product product, bool forceUpdate)
            : base(product.ProductId)
        {
            Product = product;
            ForceUpdate = forceUpdate;
        }

        public ProductUpdateTask(int productId, bool forceUpdate)
            : base(productId)
        {
            ForceUpdate = forceUpdate;
        }

        #endregion

        #region Task

        public override void DoTask(IndexManager indexManager, SearchManager searchManager, IndexWriterWrapper indexer)
        {
            if (Product == null)
            {
                LogManager.InsertLog(LogTypeEnum.AdministrationArea,
                    "Tried to index product " + ProductId + " but did not exist",
                    new Exception());
                return;
            }
            var shouldUpdate = true;
            if (!ForceUpdate)
            {
                //We shouldn't force an update, so lets check concurency
                var productQuery = new ProductQuery().WithProductID(Product.ProductId).Query;
                int total;
                var productDocument =
                    searchManager.SearchProducts(productQuery, ProductsSortBy.Relevance, 0, 1, out total).
                        SingleOrDefault();
                if (productDocument != null)
                {
                    shouldUpdate = ShouldUpdateProduct(productDocument,
                                                       DateTime.SpecifyKind(Product.UpdatedOn,
                                                                            DateTimeKind.Utc));
                }
            }
            if (shouldUpdate)
            {
                var document = Product.DocumentProduct();
                if (document != null && Product.Published && !Product.Deleted)
                {
                    var term = new Term("ProductID", Product.ProductId.ToString());
                    indexer.UpdateDocument(term, document);
                }
                else
                {
                    indexer.DeleteDocuments(new ProductQuery().WithProductID(Product.ProductId).Query);
                }

            }
        }

        public override LuceneIndexType IndexType
        {
            get { return LuceneIndexType.Product; }
        }

        public override bool RebuildIndex
        {
            get { return false; }
        }

        public override IEnumerable<Func<ProductUpdateTask, object>> ComparableProperties()
        {
            return new List<Func<ProductUpdateTask, object>> { x => x.ForceUpdate };
        }

        public override void UpdateTask(ProductUpdateTask newTask)
        {
            if (!ForceUpdate)
            {
                ForceUpdate = newTask.ForceUpdate;
            }
        }

        #endregion

        #region Helpers

        public bool ShouldUpdateProduct(Document productDocument, DateTime updatedOn)
        {
            var documentUpdateOn = DateTime.SpecifyKind(productDocument.GetValue("UpdatedOn", DateTime.MinValue), DateTimeKind.Utc);
            var productUpdateOn = updatedOn.ToString(new CultureInfo("en-US")).ConvertTo<DateTime>();//I am puting the entitie's value through the same rince cycle that our index datetime has gone through to ensure that there state will be the same.
            var result = documentUpdateOn < productUpdateOn;
            return result;
        }

        #endregion
    }
}
